/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

#ifndef _THRIFT_TRANSPORT_TSSLCONTEXT_H_
#define _THRIFT_TRANSPORT_TSSLCONTEXT_H_ 1

#include <list>
#include <map>
#include <vector>
#include <string>

#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <openssl/ssl.h>
#include <openssl/tls1.h>

namespace apache {
namespace thrift {
namespace transport {

/**
 * Override the default password collector.
 */
class PasswordCollector
{
public:
  virtual ~PasswordCollector()
  {
  
  }
  /**
   * Interface for customizing how to collect private key password.
   *
   * By default, OpenSSL prints a prompt on screen and request for password
   * while loading private key. To implement a custom password collector,
   * implement this interface and register it with TSSLSocketFactory.
   *
   * @param password Pass collected password back to OpenSSL
   * @param size     Maximum length of password including nullptr character
   */
  virtual void getPassword(std::string& /* password */, size_t /* size */) = 0;

  /**
   * Return a description of this collector for logging purposes
   */
  virtual std::string describe() const = 0;
};

/**
 * Wrap OpenSSL SSL_CTX into a class.
 */
class TSSLContext
{
public:

  enum SSLVersion {
     SSLv3,
     TLSv1
  };

  /**
   * Constructor.
   *
   * @param version The lowest or oldest SSL version to support.
   */
  explicit TSSLContext(SSLVersion version = TLSv1);
  virtual ~TSSLContext();

  /**
   * Set default ciphers to be used in SSL handshake process.
   *
   * @param ciphers A list of ciphers to use for TLSv1.0
   */
  virtual void ciphers(const std::string& ciphers);

  /**
   * Low-level method that attempts to set the provided ciphers on the
   * SSL_CTX object, and throws if something goes wrong.
   */
  virtual void setCiphersOrThrow(const std::string& ciphers);

  /**
   * Method to fetch Verification mode determined by the options
   * set using setVerificationOption.
   *
   * @return mode flags that can be used with SSL_set_verify
   */
  virtual boost::asio::ssl::verify_mode getVerificationMode();

  /**
   * Enable/Disable authentication. Peer name validation can only be done
   * if checkPeerCert is true.
   *
   * @param checkPeerCert If true, require peer to present valid certificate
   * @param checkPeerName If true, validate that the certificate common name
   *                      or alternate name(s) of peer matches the hostname
   *                      used to connect.
   * @param peerName      If non-empty, validate that the certificate common
   *                      name of peer matches the given string (altername
   *                      name(s) are not used in this case).
   */
  virtual void authenticate(bool checkPeerCert, bool checkPeerName,
                            const std::string& peerName = "");
  /**
   * Load server certificate.
   *
   * @param path   Path to the certificate file
   * @param format Certificate file format
   */
  virtual void loadCertificate(const std::string& path, const std::string& format = "PEM");
  /**
   * Load private key.
   *
   * @param path   Path to the private key file
   * @param format Private key file format
   */
  virtual void loadPrivateKey(const std::string& path, const std::string& format = "PEM");
  /**
   * Load trusted certificates from specified file.
   *
   * @param path Path to trusted certificate file
   */
  virtual void loadTrustedCertificates(const std::string& path);
  /**
   * Load trusted certificates from specified X509 certificate store.
   *
   * @param store X509 certificate store.
   */
  virtual void loadTrustedCertificates(X509_STORE* store);
  /**
   * Load a client CA list for validating clients
   */
  virtual void loadClientCAList(const std::string& path);
  /**
   * Override default OpenSSL password collector.
   *
   * @param collector Instance of user defined password collector
   */
  virtual void passwordCollector(const boost::shared_ptr<PasswordCollector>& collector);
  /**
   * Obtain password collector.
   *
   * @return User defined password collector
   */
  virtual boost::shared_ptr<PasswordCollector> passwordCollector()
  {
    return collector_;
  }

  /**
   * Gets the underlying SSL_CTX for advanced usage
   */
  boost::asio::ssl::context& getSSLCtx() const
  {
    return *ctx_;
  }

  /**
   * Examine OpenSSL's error stack, and return a string description of the
   * errors.
   *
   * This operation removes the errors from OpenSSL's error stack.
   */
  static std::string getSSLErrors();

  bool checkPeerName()
  {
    return checkPeerName_;
  }
  
  std::string peerFixedName()
  {
    return peerFixedName_;
  }

  /**
   * Helper to match a hostname versus a pattern.
   */
  static bool matchName(const char* host, const char* pattern, int size);

protected:
   boost::shared_ptr<boost::asio::ssl::context> ctx_;
   std::string passwordCallback(size_t size, boost::asio::ssl::context::password_purpose purpose);

private:
  boost::asio::ssl::verify_mode verifyMode_;

  bool checkPeerName_;
  std::string peerFixedName_;
  boost::shared_ptr<PasswordCollector> collector_;
  std::string providedCiphersString_;
};

typedef boost::shared_ptr<TSSLContext> SSLContextPtr;

std::ostream& operator<<(std::ostream& os, const PasswordCollector& collector);

}
}
}

#endif
